package cn.lg.soar.redis.aspect.clusterlock;

import cn.lg.soar.core.aspect.SpelExpressionUtils;
import jodd.util.StringUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;

/**
 * @author luguoxiang
 * @date 2022/6/6
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
interface IConfig {

    ExpressionParser parser = new SpelExpressionParser();

    /**
     * 获取锁名称
     * @param point
     * @return
     */
    String lockName(ProceedingJoinPoint point);

    /**
     * 构建配置
     * @param point
     * @param name
     * @param key
     * @return
     */
    static IConfig build(ProceedingJoinPoint point, String name, String key) {
        // 锁粒度是整个方法
        if (StringUtil.isEmpty(key)) {
            return new DefaultConfig(name);
        }
        try {
            // 锁粒度是参数列表的某个参数
            Integer argsIndex = Integer.parseInt(key);
            return new IndexConfig(name, point, argsIndex);
        } catch (Exception e) {
            return new ElConfig(name, point, key);
        }
    }

    /**
     * 默认配置
     */
    class DefaultConfig implements IConfig {

        private final String prefix; // 锁前缀

        public DefaultConfig(String prefix) {
            this.prefix = prefix;
        }

        @Override
        public String lockName(ProceedingJoinPoint point) {
            return this.prefix;
        }

    }

    /**
     * 参数索引方式配置
     */
    class IndexConfig implements IConfig {

        private final String prefix; // 锁前缀

        private final Integer argsIndex; // 参数索引

        public IndexConfig(String prefix, ProceedingJoinPoint point, Integer argsIndex) {
            Object[] args = point.getArgs();
            if (argsIndex < 0) {
                argsIndex = args.length + argsIndex;
            }
            if (argsIndex < 0 || argsIndex >= args.length) {
                throw new RuntimeException("分布式锁key设置错误");
            }

            this.prefix = prefix + ":";
            this.argsIndex = argsIndex;
        }

        @Override
        public String lockName(ProceedingJoinPoint point) {
            return this.prefix + point.getArgs()[ this.argsIndex ];
        }

    }


    /**
     * el表达式方式配置
     */
    class ElConfig implements IConfig{

        private final String prefix; // 锁前缀
        private final String[] paramNames; // 参数列表参数名数组
        private final Expression expression;

        public ElConfig(String prefix, ProceedingJoinPoint point, String key) {
            this.prefix = prefix + ":";
            // 锁粒度由 el 表达式得出
            Method method = ((MethodSignature) point.getSignature()).getMethod();
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            this.paramNames = u.getParameterNames(method);
            this.expression = SpelExpressionUtils.getExpression(key);
        }

        @Override
        public String lockName(ProceedingJoinPoint point) {
            EvaluationContext context = new StandardEvaluationContext();
            Object[] arguments = point.getArgs();
            for(int i=0; i < arguments.length; i++){
                context.setVariable(this.paramNames[i], arguments[i]);
            }
            return this.prefix + this.expression.getValue(context, String.class);
        }

    }
}
